using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Practices.Unity;
using Enumerable = System.Linq.Enumerable;

namespace FireStarterModeller.Logic
{
    public class AutoResolvingUnityContainer : UnityContainer
    {
        private Func<Type, bool> Where { get; set; }

        public AutoResolvingUnityContainer(Func<Type, bool> where)
        {
            Where = where;
        }

        public AutoResolvingUnityContainer()
        {
        }
        public override object Resolve(Type t, string name)
        {
            try
            {
                return base.Resolve(t, name);
            }
            catch (ResolutionFailedException)
            {
                Debug.WriteLine(string.Format("AutoResolving Of Type:{0}", t));
                if (!t.IsInterface && !t.IsAbstract )
                {
                    base.RegisterType(t, t);
                    return base.Resolve(t);
                }
//            var typeSource = new AppDomainTypeSource(type => !type.Name.Contains("Proxy"));//Ensure you do not get a Mock Object
                var typeSource = new AppDomainTypeSource(Where);
                var firstSubType = typeSource.GetTypes().Where(t.IsAssignableFrom).FirstOrDefault();
                if (firstSubType != null)
                {
                    RegisterType(t, firstSubType);
                    return base.Resolve(t);
                }
                throw;
            }
        }

        private class AppDomainTypeSource
        {
            private Func<Type, bool> Where { get; set; }

            public AppDomainTypeSource(Func<Type, bool> where)
            {
                Where = where;
            }
/*
            public AppDomainTypeSource()
            {
            }*/

            public IEnumerable<Type> GetTypes()
            {
                return this.Where == null
                        ? TypesImplementing()
                        : TypesImplementing().Where(this.Where);
            }

            private static IEnumerable<Type> TypesImplementing()
            {
                return AppDomain
                    .CurrentDomain
                    .GetAssemblies()
                    .SelectMany(assembly => assembly.GetTypes())
                    .Where(type => !type.IsInterface && !type.IsAbstract);
            }
        }
    }

}